package ch01.GridCollision {
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.utils.getTimer;
	import flash.utils.setTimeout;

	import tools.Ball;

	/**
	 * 基于网格的碰撞检测，网格检测例子一
     * 
     * 使用这种方法的前提条件是格子大小至少得能容下对象中最大的那个。如果有球比格子大，上述方法就无效了。
     * 需要知道的是，这种处理方式并不一定是最好的，但它确实完成了对碰撞检测次数的有效限制，不必对所有对象都进行两两之间的检测。
	 */
	public class GridCollision extends Sprite {
		//网格大小一定不能小于最大的对象大小,所以网格两倍于半径;
		private const GRID_SIZE:Number = 50;
		private const RADIUS:Number = 25;
		private var _balls:Array;
		private var _grid:Array;
		private var _numBalls:int = 100;
		private var _numChecks:int = 0;
		private var _w:Number = 0.0;
		private var _h:Number = 0.0;


		public function GridCollision(){
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;

			setTimeout(callLaterHandler, 500);
		}

		private function callLaterHandler(event:Event = null):void {
			var _time:Number = getTimer();
			_w = stage.stageWidth;
			_h = stage.stageWidth;
			makeBalls();
			makeGrid();
			drawGrid();
			assignBallsToGrid();
			checkGrid();
			trace("3:网格检测 renderer time:", (getTimer() - _time), "测试次数:", _numChecks);
			_time = getTimer();
            _numChecks = 0;
			basicCheck();
			trace("3:庶民检测 renderer time:", (getTimer() - _time), "测试次数:", _numChecks);
		}

		private function makeBalls():void {
			_balls = new Array();
			for (var i:int = 0; i < _numBalls; i++){
				// 创建出一个球，然后把它加入显示列表以及数组
				var ball:Ball = new Ball(RADIUS);
				//var ball:Ball = new Ball(Math.random() * RADIUS);
				ball.x = Math.random() * _w;
				ball.y = Math.random() * _h;
				addChild(ball);
				_balls.push(ball);
			}
		}

		private function makeGrid():void {
			_grid = new Array();
			var i_loop:int = _w / GRID_SIZE;
			var j_loop:int = _h / GRID_SIZE;
			// 场景宽度 / 格子大小 = 网格列数
			for (var i:int = 0; i < i_loop; i++){
				_grid[i] = new Array();
				// 场景高度 / 格子大小 = 网格行数
				for (var j:int = 0; j < j_loop; j++){
					_grid[i][j] = new Array();
				}
			}
		}

		/**画网格函数，纯粹是辅助用的。实际开发中一般用不到*/
		private function drawGrid():void {
			// 画出行列线
			graphics.lineStyle(0, .5);
			for (var i:int = 0; i <= _w; i += GRID_SIZE){
				graphics.moveTo(i, 0);
				graphics.lineTo(i, _h);
			}
			for (i = 0; i <= _h; i += GRID_SIZE){
				graphics.moveTo(0, i);
				graphics.lineTo(_w, i);
			}
		}

		private function assignBallsToGrid():void {
			for (var i:int = 0; i < _numBalls; i++){
				// 球的位置除以格子大小，得到该球所在网格的行列数
				var ball:Ball = _balls[i] as Ball;
				var xpos:int = Math.floor(ball.x / GRID_SIZE);
				var ypos:int = Math.floor(ball.y / GRID_SIZE);
				_grid[xpos][ypos].push(ball);
			}
		}

		private function checkGrid():void {
			var i_loop:int = _grid.length;
			var j_loop:int = 0;
			for (var i:int = 0; i < i_loop; i++){
				j_loop = _grid[i].length;
				for (var j:int = 0; j < j_loop; j++){
					// 检测当前格子内的对象间是否发生碰撞
					checkOneCell(i, j);
					checkTwoCells(i, j, i + 1, j); // 右边的格子
					checkTwoCells(i, j, i - 1, j + 1); // 左下角的格子
					checkTwoCells(i, j, i, j + 1); // 下边的格子
					checkTwoCells(i, j, i + 1, j + 1); // 右下角的格子
				}
			}
		}

		/**
		 * 检测当前格子内的对象间是否发生碰撞
		 * @param	x
		 * @param	y
		 */
		private function checkOneCell(x:int, y:int):void {
			// 检测当前格子内所有的对象
			var cell:Array = _grid[x][y] as Array;
			var i_loop:int = cell.length - 1;
			var j_loop:int = cell.length;
			for (var i:int = 0; i < i_loop; i++){
				var ballA:Ball = cell[i] as Ball;
				for (var j:int = i + 1; j < j_loop; j++){
					var ballB:Ball = cell[j] as Ball;
					checkCollision(ballA, ballB);
				}
			}
		}

		/**
		 *
		 * @param	x1
		 * @param	y1
		 * @param	x2
		 * @param	y2
		 */
		private function checkTwoCells(x1:int, y1:int, x2:int, y2:int):void {
			// 确保要检测的格子存在
			if (x2 < 0){
				return;
			}
			if (x2 >= _grid.length){
				return;
			}
			if (y2 >= _grid[x2].length){
				return;
			}
			var cell0:Array = _grid[x1][y1] as Array;
			var cell1:Array = _grid[x2][y2] as Array;
			var i_loop:int = cell0.length;
			var j_loop:int = cell1.length;
			// 检测当前格子和邻接格子内所有的对象
			for (var i:int = 0; i < i_loop; i++){
				var ballA:Ball = cell0[i] as Ball;
				for (var j:int = 0; j < j_loop; j++){
					var ballB:Ball = cell1[j] as Ball;
					checkCollision(ballA, ballB);
				}
			}
		}

		/**
		 * 碰撞检测
		 * @param	ballA
		 * @param	ballB
		 */
		private function checkCollision(ballA:Ball, ballB:Ball):void {
			// 判断距离的碰撞检测
			_numChecks++;
			var dx:Number = ballB.x - ballA.x;
			var dy:Number = ballB.y - ballA.y;
			var dist:Number = Math.sqrt(dx * dx + dy * dy);
			if (dist < ballA.radius + ballB.radius){
				ballA.color = 0xFF0000;
				ballB.color = 0xFF0000;
			}
		}

		/**
		 * 庶民检测，与网格检测做对比
		 */
		private function basicCheck():void {
			var i_loop:int = _balls.length - 1;
			var j_loop:int = _balls.length;
			for (var i:int = 0; i < i_loop; i++){
				var ballA:Ball = _balls[i] as Ball;
				for (var j:int = i + 1; j < j_loop; j++){
					var ballB:Ball = _balls[j] as Ball;
					checkCollision(ballA, ballB);
				}
			}
		}
	}
}